package com.zonta.finance.serivce.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.ObjUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.zonta.finance.common.dto.TokenResponse;
import com.zonta.finance.common.exception.BizException;
import com.zonta.finance.common.exception.ParameterException;

import com.zonta.finance.constant.RedisKeyConstant;
import com.zonta.finance.domain.po.Member;
import com.zonta.finance.domain.po.MemberBindPhone;
import com.zonta.finance.domain.form.PhoneRegisterForm;
import com.zonta.finance.domain.po.MemberBindWxOpenid;
import com.zonta.finance.domain.vo.GenerateMpRegCodeVo;
import com.zonta.finance.enums.SmsCodeTypeEnum;
import com.zonta.finance.serivce.*;
import com.zonta.finance.wx.config.WxConfig;
import com.zonta.finance.wx.dto.AccessTokenResult;
import com.zonta.finance.wx.dto.MpQrCodeCreateRequest;
import com.zonta.finance.wx.dto.MpQrCodeCreateResult;
import com.zonta.finance.wx.dto.MpSubscribeEventRequest;
import com.zonta.finance.wx.service.WXService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.logging.log4j.util.Strings;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.context.event.EventListener;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Service;
import org.springframework.transaction.support.TransactionTemplate;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.client.WebClient;

import java.util.concurrent.TimeUnit;

@Service
@Slf4j
@RequiredArgsConstructor
public class MemberRegisterServiceImpl implements IMemberRegisterService {
    private final MemberLoginService memberLoginService;
    private final RedissonClient redissonClient;
    private final IMemberBindPhoneService memberBindPhoneService;
    private final TransactionTemplate transactionTemplate;
    private final ITenantService tenantService;
    private final IMemberService memberService;
    private final WebClient webClient;
    private final WXService wxService;
    private final WxConfig wxConfig;
    private final RedisTemplate redisTemplate;
    private final IMemberBindWxOpenidService memberBindWxOpenidService;
    /**
     * 手机号注册
     * @param form 手机号 密码 确认密码 短信验证码
     * @return
     */
    @Override
    public Long phoneRegister(PhoneRegisterForm form) {
        // 判断两次密码是否一致
        if (!form.getPassword().equals(form.getConfirmPassword())){
            throw new ParameterException("两次输入密码不一致");
        }
        // 校验短信验证码
        memberLoginService.checkSmsCode(form.getPhone(),form.getSmsCode(), SmsCodeTypeEnum.REGISTER.getCode());
        // 通过手机号 获取一把锁
        RLock rlock = redissonClient.getLock("PHONE_REG" + form.getPhone());
        try {
            rlock.lock();
            MemberBindPhone memberBindPhone = memberBindPhoneService.getOne(new LambdaQueryWrapper<MemberBindPhone>().eq(MemberBindPhone::getPhone, form.getPhone()));
            if (ObjUtil.isNotEmpty(memberBindPhone)){
                log.warn("手机号：{}已注册",form.getPhone());
                throw new BizException("该手机号已注册");
            }
            // 这里不推荐使用注解事务，因为上面的代码没有用到事务，注解事务会全部包揽，变成大事务
            transactionTemplate.execute(transactionStatus-> {
                Long tenantId = tenantService.add();
                Long id = memberService.register(tenantId);
                memberBindPhoneService.register(form.getPhone(),id,form.getPassword());
                return id;
            });
        }catch (Exception e){
            e.printStackTrace();
            throw new BizException("注册失败");
        }finally {
            rlock.unlock();
        }
        return 0L;
    }

    /**
     * 生成微信公众号二维码（关注注册）
     * @param clientId 客户端id
     * @return
     */
    @Override
    public GenerateMpRegCodeVo generateMpRegCode(String clientId) {
        // 根据id和密钥获取微信token
        //AccessTokenResult accessTokenResult = wxService.getMpAccessToken(wxConfig.getMp().getAppId(), wxConfig.getMp().getSecret());
        AccessTokenResult accessTokenResult = wxService.getMpAccessTokenByCache(wxConfig.getMp().getAppId());

        // 生成请求参数并设置值
        MpQrCodeCreateRequest request = new MpQrCodeCreateRequest();
        //设置过期时间
        request.setExpireSeconds(wxConfig.getMp().getCodeExpire());
        // 设置二维码类型，QR_STR_SCENE临时二维码
        request.setActionName("QR_STR_SCENE");
        request.setActionInfo(request.new ActionInfo());
        // 设置场景
        request.getActionInfo().setScene(request.new scene());
        request.getActionInfo().getScene().setSceneStr("ScanReg_" + wxConfig.getMp().getAppId() + "_" + clientId);
        // 创建二维码
        MpQrCodeCreateResult result = wxService.createMpQrcodeCreate(accessTokenResult.getAccessToken(), request);
        // 对象转换
        GenerateMpRegCodeVo generateMpRegCodeVo = BeanUtil.copyProperties(result, GenerateMpRegCodeVo.class);
        generateMpRegCodeVo.setClientId(clientId);
        return generateMpRegCodeVo;
    }
    @EventListener
    public void handleMpSubscribeEventRequest(MpSubscribeEventRequest mpSubscribeEventRequest) {
        log.info("接收到消息：MpSubscribeEventRequest：{}", mpSubscribeEventRequest.toString());
        log.info("0:{}", mpSubscribeEventRequest.getEvent());
        if ("subscribe".equals(mpSubscribeEventRequest.getEvent())
                && Strings.isNotBlank(mpSubscribeEventRequest.getEventKey())) {
            String[] keys = mpSubscribeEventRequest.getEventKey().split("_");
            if ("qrscene".equals(keys[0]) && "ScanReg".equals(keys[1])) {
                log.info("AppId：{}，ClientId：{}", keys[2], keys[3]);
                registerByMpOpenId(keys[2], keys[3], mpSubscribeEventRequest.getToUserName());
                return;
            }
        }
        if ("SCAN".equals(mpSubscribeEventRequest.getEvent()) &&
                Strings.isNotBlank(mpSubscribeEventRequest.getEventKey())) {
            String[] keys = mpSubscribeEventRequest.getEventKey().split("_");
            if ("ScanReg".equals(keys[0])) {
                log.info("AppId：{}，ClientId：{}", keys[1], keys[2]);
                registerByMpOpenId(keys[1], keys[2], mpSubscribeEventRequest.getToUserName());
                return;
            }
        }
    }
    /**
     * 根据微信openId注册用户
     * @param appId //小程序或者公众号appid
     * @param clientId 客户端id
     * @param openId 微信用户openId
     * @return
     */
    @Override
    public TokenResponse registerByMpOpenId(String appId, String clientId, String openId) {
        long memberId = scReg(appId, openId);
        Member member = memberService.getById(memberId);
        TokenResponse tokenResponse = memberLoginService.loginSuccess(member, clientId);
        redisTemplate.opsForValue().set(RedisKeyConstant.CLIENT_TOKEN_KEY + clientId, tokenResponse, 10, TimeUnit.MINUTES);
        return tokenResponse;
    }

    private long scReg(String appId, String openId) {
        // 查询数据库是否注册
        MemberBindWxOpenid memberBindWxOpenid = memberBindWxOpenidService.getOne(new LambdaQueryWrapper<MemberBindWxOpenid>().eq(MemberBindWxOpenid::getAppId, appId).eq(MemberBindWxOpenid::getOpenId, openId));
        if (ObjUtil.isNotEmpty(memberBindWxOpenid)){
            return memberBindWxOpenid.getMemberId();

        }
        Long memberId=transactionTemplate.execute(transactionStatus->{
            long tenantId = tenantService.add();
            long id = memberService.register(tenantId);
            memberBindWxOpenidService.register(appId, openId, id);
            return id;
        });
        if (memberId==null){
            throw new BizException("注册失败!!!");
        }
        return memberId;
    }
}
